# -*- coding: utf-8 -*-

"""
PDF矩形拼板工具
从PDF提取的矩形中计算最佳拼板方案
宽度限制：1米(1000mm)
间距：10mm
矩形允许旋转
目标：计算最短的拼接长度
"""

import os
import json
import argparse
import math
from typing import List, Dict, Tuple

class RectangleItem:
    """矩形物品类，用于表示要拼板的矩形"""
    def __init__(self, width, height, index=None):
        self.width = width
        self.height = height
        self.index = index
        self.x = 0  # 放置位置的x坐标
        self.y = 0  # 放置位置的y坐标
        self.rotated = False  # 是否旋转
        self.area = width * height
        
    def rotate(self):
        """旋转矩形，交换宽高"""
        self.width, self.height = self.height, self.width
        self.rotated = not self.rotated
        
    def get_dimensions(self):
        """获取矩形的尺寸"""
        return self.width, self.height
        
    def __repr__(self):
        return f"Rectangle(width={self.width}, height={self.height}, index={self.index}, rotated={self.rotated})"

class Shelf:
    """货架类，用于分层装箱算法"""
    def __init__(self, height, width_limit, spacing):
        self.height = height
        self.width_limit = width_limit
        self.spacing = spacing
        self.remaining_width = width_limit
        self.items = []
        self.y_position = 0
        
    def can_place(self, item):
        """检查是否可以放置物品"""
        # 考虑间距，如果不是第一个物品，需要增加间距
        required_width = item.width
        if self.items:
            required_width += self.spacing
            
        return required_width <= self.remaining_width
        
    def place_item(self, item):
        """放置物品到货架上"""
        if self.items:
            # 如果不是第一个物品，位置需要加上前面物品的宽度和间距
            item.x = self.items[-1].x + self.items[-1].width + self.spacing
        else:
            item.x = 0
            
        item.y = self.y_position
        self.items.append(item)
        
        # 更新剩余宽度，如果不是第一个物品，需要减去间距
        if self.items:
            self.remaining_width -= item.width
            if len(self.items) > 1:
                self.remaining_width -= self.spacing

class RectangleNesting:
    """矩形拼板类，实现矩形装箱算法"""
    def __init__(self, width_limit=1000, spacing=10):
        self.width_limit = width_limit  # 宽度限制，默认1000mm
        self.spacing = spacing  # 间距，默认10mm
        self.items = []  # 所有要拼板的矩形
        self.shelves = []  # 货架列表
        self.total_length = 0  # 总长度
    
    def load_from_json(self, json_path):
        """从JSON文件加载矩形数据"""
        try:
            with open(json_path, 'r', encoding='utf-8') as f:
                data = json.load(f)
                
            # 从所有页面提取矩形
            for page in data.get('pages', []):
                for idx, rect in enumerate(page.get('detected_rectangles', [])):
                    # 获取矩形尺寸
                    width = rect.get('width', 0)
                    height = rect.get('height', 0)
                    
                    # 转换为mm单位（假设原始单位是像素，这里需要根据实际情况调整）
                    # 注意：这里的转换系数需要根据实际PDF的DPI进行调整
                    # 例如，如果PDF是300DPI，那么1英寸=300像素=25.4mm
                    # 这里简化处理，直接使用像素值作为mm值
                    # 实际应用中需要根据PDF的DPI进行正确转换
                    width_mm = width
                    height_mm = height
                    
                    # 创建矩形物品
                    item = RectangleItem(width_mm, height_mm, index=idx)
                    self.items.append(item)
                    
            print(f"成功加载 {len(self.items)} 个矩形")
            return True
        except Exception as e:
            print(f"加载JSON文件时出错: {str(e)}")
            return False
    
    def load_from_directory(self, directory_path):
        """从目录中的图像文件加载矩形数据"""
        try:
            # 尝试导入PIL库
            try:
                from PIL import Image
                has_pil = True
            except ImportError:
                has_pil = False
                print("警告：未安装PIL库(Pillow)，无法读取图像尺寸")
                print("请安装：pip install Pillow")
                return False
            
            if not has_pil:
                return False
            
            # 获取目录中的所有PNG文件
            png_files = [f for f in os.listdir(directory_path) if f.endswith('.png') and 'rectangle' in f]
            
            for idx, png_file in enumerate(png_files):
                file_path = os.path.join(directory_path, png_file)
                
                # 打开图像获取尺寸
                with Image.open(file_path) as img:
                    width, height = img.size
                    
                    # 转换为mm单位（同样需要根据实际DPI调整）
                    width_mm = width
                    height_mm = height
                    
                    # 创建矩形物品
                    item = RectangleItem(width_mm, height_mm, index=idx)
                    self.items.append(item)
                    
            print(f"成功加载 {len(self.items)} 个矩形图像")
            return True
        except Exception as e:
            print(f"加载目录时出错: {str(e)}")
            return False
    
    def sort_items(self):
        """对矩形进行排序，大的矩形先放置"""
        # 按面积降序排序
        self.items.sort(key=lambda x: x.area, reverse=True)
    
    def optimize_rotation(self):
        """优化矩形旋转，使宽度尽可能适合宽度限制"""
        for item in self.items:
            # 如果矩形宽度超过限制，旋转它
            if item.width > self.width_limit and item.height <= self.width_limit:
                item.rotate()
            # 如果两种方向都超过限制，选择宽度较小的方向
            elif item.width > self.width_limit and item.height > self.width_limit:
                if item.width > item.height:
                    item.rotate()
    
    def nest_rectangles(self):
        """执行矩形拼板算法"""
        if not self.items:
            print("没有可拼板的矩形")
            return
        
        # 排序矩形
        self.sort_items()
        
        # 优化旋转
        self.optimize_rotation()
        
        # 初始化货架
        current_y = 0
        
        # 对于每个矩形，尝试放置到现有的货架，或者创建新货架
        for item in self.items:
            placed = False
            
            # 尝试放置到现有货架
            for shelf in self.shelves:
                # 尝试两种方向
                for _ in range(2):
                    if shelf.can_place(item):
                        shelf.place_item(item)
                        placed = True
                        break
                    # 如果不能放置，尝试旋转
                    item.rotate()
                
                if placed:
                    break
            
            # 如果没有找到合适的货架，创建新货架
            if not placed:
                # 确保矩形宽度不超过限制
                if item.width > self.width_limit:
                    # 尝试旋转
                    item.rotate()
                    if item.width > self.width_limit:
                        print(f"警告：矩形 {item.index} 无法放置，尺寸过大")
                        continue
                
                # 创建新货架，高度为矩形高度加上间距
                new_shelf = Shelf(item.height, self.width_limit, self.spacing)
                new_shelf.y_position = current_y
                new_shelf.place_item(item)
                self.shelves.append(new_shelf)
                
                # 更新当前Y位置
                current_y += item.height + self.spacing
        
        # 计算总长度（减去最后一个间距）
        if self.shelves:
            self.total_length = current_y - self.spacing
        
        print(f"拼板完成，总长度: {self.total_length:.2f}mm")
        
        # 输出每个矩形的位置信息
        for item in self.items:
            print(f"矩形 {item.index}: 位置=({item.x:.2f}, {item.y:.2f}), 尺寸=({item.width:.2f}x{item.height:.2f}), 旋转={item.rotated}")
    
    def visualize_nesting(self, output_path=None):
        """可视化拼板结果（当前已禁用，需要安装matplotlib和numpy库）"""
        print("可视化功能当前已禁用。如需启用，请安装matplotlib和numpy库：")
        print("pip install matplotlib numpy")
        return
    
    def export_results(self, output_path):
        """导出拼板结果到JSON文件"""
        results = {
            "width_limit": self.width_limit,
            "spacing": self.spacing,
            "total_length": self.total_length,
            "rectangles": []
        }
        
        for item in self.items:
            rect_data = {
                "index": item.index,
                "width": item.width,
                "height": item.height,
                "x": item.x,
                "y": item.y,
                "rotated": item.rotated
            }
            results["rectangles"].append(rect_data)
        
        try:
            with open(output_path, 'w', encoding='utf-8') as f:
                json.dump(results, f, ensure_ascii=False, indent=2)
            print(f"拼板结果已导出到: {output_path}")
        except Exception as e:
            print(f"导出结果时出错: {str(e)}")


def main():
    """主函数，提供命令行接口"""
    parser = argparse.ArgumentParser(description='PDF矩形拼板工具 - 计算最佳拼板方案')
    
    # 输入选项
    parser.add_argument('--json-path', type=str, required=True, help='JSON结果文件路径')
    
    # 输出选项
    parser.add_argument('--export-json', type=str, help='拼板结果JSON导出路径')
    
    # 拼板参数
    parser.add_argument('--width-limit', type=float, default=1000, help='拼板宽度限制(mm)，默认值为1000mm')
    parser.add_argument('--spacing', type=float, default=10, help='矩形间距(mm)，默认值为10mm')
    
    args = parser.parse_args()
    
    # 创建拼板实例
    nesting = RectangleNesting(width_limit=args.width_limit, spacing=args.spacing)
    
    # 加载矩形数据（只支持JSON文件方式）
    success = nesting.load_from_json(args.json_path)
    
    if success:
        # 执行拼板
        nesting.nest_rectangles()
        
        # 导出结果
        if args.export_json:
            nesting.export_results(args.export_json)
    else:
        print("加载矩形数据失败，程序退出")
        return 1
        
    return 0

if __name__ == '__main__':
    exit_code = main()
    import sys
    sys.exit(exit_code)